from typing import Set
from Cut import Cut
from GetStopWords import GetStopWrods
import os
import random
import numpy as np

def takeFirst(item):
    if item == 0:
        return 1000000000000000.0
    return 1.0 / item[0]

def sampleNFold(totalNumber, N = 5):
    container = [i for i in range(totalNumber)]
    numberPerFold = int(totalNumber / N)
    sampledContainer = []
    for i in range(N):
        c = random.sample(container, numberPerFold)
        sampledContainer.append(c)
        for item in c:
            container.remove(item)
    return sampledContainer

class SetProbability():

    def __init__(self, N = 5):
        self.N = N
        self.sampledContainer = sampleNFold(300, self.N)
        self.defaults = {}
        self.classPaths = []
        self.wordsContainer = []
        self.wordsDictionary = {}
        CLASSES_PATH = os.path.dirname(os.path.abspath(__file__)) + "/" + "20_newsgroups_subset" + "/"
        CLASSES_LIST = os.listdir(CLASSES_PATH)
        for dir in CLASSES_LIST:
            classPath = CLASSES_PATH + dir + "/"
            self.classPaths.append(classPath)
            files = os.listdir(classPath)
            self.__dict__[dir + "files"] = files
            # print(len(files))
            self.defaults[dir + "WordMatrix"] = []
            self.defaults[dir + "BoolMatrix"] = []
            self.defaults[dir + "WordContainer"] = []
            self.defaults[dir + "WordDictionary"] = {}
        self.__dict__.update(self.defaults)

    def clearLastFold(self):
        self.defaults = {}
        # self.classPaths = []
        self.wordsContainer = []
        self.wordsDictionary = {}
        CLASSES_PATH = os.path.dirname(os.path.abspath(__file__)) + "/" + "20_newsgroups_subset" + "/"
        CLASSES_LIST = os.listdir(CLASSES_PATH)
        for dir in CLASSES_LIST:
            self.defaults[dir + "WordMatrix"] = []
            self.defaults[dir + "BoolMatrix"] = []
            self.defaults[dir + "WordContainer"] = []
            self.defaults[dir + "WordDictionary"] = {}
        self.__dict__.update(self.defaults)


    def setWordsContainer(self, kth):

        CLASSES_PATH = os.path.dirname(os.path.abspath(__file__)) + "/" + "20_newsgroups_subset" + "/"
        CLASSES_LIST = os.listdir(CLASSES_PATH)
        
        self.clearLastFold()

        for dir in CLASSES_LIST:
            # print(CLASSES_PATH + dir + "/")
            classPath = CLASSES_PATH + dir + "/"
            # self.classPaths.append(classPath)
            files = self.__dict__[dir + "files"]
            flagOfNumber = 0
            for file in files:
                if flagOfNumber in self.sampledContainer[kth]:
                    flagOfNumber += 1
                    continue
                flagOfNumber += 1
                filepath = classPath + file
                stopWordsPath = "stopwords.txt"
                g = GetStopWrods()
                stopWords = g.Call(stopWordsPath)
                c = Cut(stopWords)
                wordsList = c.Call(filepath)
                self.__dict__[dir + "WordMatrix"].append(wordsList)
                self.__dict__[dir + "WordContainer"].extend(wordsList)
                self.__dict__[dir + "WordContainer"] = list(set(self.__dict__[dir + "WordContainer"]))
                self.wordsContainer.extend(wordsList)
                self.wordsContainer = list(set(self.wordsContainer))
        print(str(len(self.wordsContainer)) + "\n")

    def setWordsDictionary(self):
        print("WordsDictionary Set begin." + "\n")
        for i in range(len(self.wordsContainer)):
            self.wordsDictionary[self.wordsContainer[i]] = i
        CLASSES_PATH = os.path.dirname(os.path.abspath(__file__)) + "/" + "20_newsgroups_subset" + "/"
        CLASSES_LIST = os.listdir(CLASSES_PATH)
        for dir in CLASSES_LIST:
            for i in range(len(self.__dict__[dir + "WordContainer"])):
                self.__dict__[dir + "WordDictionary"][self.__dict__[dir + "WordContainer"][i]] = i
        print("WordsDictionary Set end." + "\n")
    
    def setBoolMatrix(self):
        print("BoolMatrix Set begin." + "\n")
        CLASSES_PATH = os.path.dirname(os.path.abspath(__file__)) + "/" + "20_newsgroups_subset" + "/"
        CLASSES_LIST = os.listdir(CLASSES_PATH)
        for dir in CLASSES_LIST:
            # print("dir \n")
            for list in self.__dict__[dir + "WordMatrix"]:
                tmpList = [0 for i in range(len(self.wordsDictionary))]
                for i in range(len(list)):
                    #if tmpList[self.wordsDictionary[list[i]]] == 0:
                    tmpList[self.wordsDictionary[list[i]]] += 1
                self.__dict__[dir + "BoolMatrix"].append(tmpList)
        print("BoolMatrix Set end." + "\n")

    # def setConditionalProbability(self):
    #     print("ConditionalProbability Set begin." + "\n")
    #     CLASSES_PATH = os.path.dirname(os.path.abspath(__file__)) + "/" + "20_newsgroups_subset" + "/"
    #     CLASSES_LIST = os.listdir(CLASSES_PATH)
    #     for dir in CLASSES_LIST:
    #         # print("dir \n")
    #         SUM = 1 * len(self.wordsDictionary)
    #         self.__dict__[dir + "CP"] = [1.0 for i in range(len(self.wordsDictionary))]
    #         for list in range(len(self.__dict__[dir + "BoolMatrix"])):
    #             for i in range(len(self.wordsDictionary)):
    #                 self.__dict__[dir + "CP"][i] = self.__dict__[dir + "CP"][i] + self.__dict__[dir + "BoolMatrix"][list][i]
    #                 SUM = SUM + self.__dict__[dir + "BoolMatrix"][list][i]
    #         self.__dict__[dir + "SUM"] = SUM
    #         for i in range(len(self.wordsDictionary)):
    #             self.__dict__[dir + "CP"][i] = self.__dict__[dir + "CP"][i] / SUM
    #         if 0.0 in self.__dict__[dir + "CP"]:
    #             print("there is 0 in {}.".format(dir))
    #     print("ConditionalProbability Set end." + "\n")
    
    def train(self):
        for i in range(self.N):
            self.setWordsContainer(i)
            self.setWordsDictionary()
            self.setBoolMatrix()
            # self.setConditionalProbability()
            self.verify(i, 5)
            predClass, predProbability = self.predict("52558", 20)
            print(predClass, predProbability)

    def predict2(self, filepath):
        print("Predict begin." + "\n")
        stopWordsPath = "stopwords.txt"
        g = GetStopWrods()
        stopWords = g.Call(stopWordsPath)
        c = Cut(stopWords)
        wordsList = c.Call(filepath)
        CLASSES_PATH = os.path.dirname(os.path.abspath(__file__)) + "/" + "20_newsgroups_subset" + "/"
        CLASSES_LIST = os.listdir(CLASSES_PATH)
        maxProbability = 0.0
        maxClass = None
        minUnseen = 10000
        for dir in CLASSES_LIST:
            tmpProbability = 1.0
            unseen = 0
            for i in range(len(wordsList)):
                if wordsList[i] in self.__dict__[dir + "WordDictionary"]:
                    tmpProbability = tmpProbability * self.__dict__[dir + "CP"][self.__dict__[dir + "WordDictionary"][wordsList[i]]]
                else:
                    tmpProbability = tmpProbability / (self.__dict__[dir + "SUM"] + 1)
                    unseen += 1
                    # print("{} Unseen words {}".format(dir, unseen))
                    # print(tmpProbability)
            if tmpProbability > maxProbability:
                maxProbability = tmpProbability
                maxClass = dir
            if maxProbability == 0:
                if unseen < minUnseen:
                    maxClass = dir
                    minUnseen = unseen
        print("Predict end." + "\n")
        return maxClass, maxProbability

    def verify(self, kth, kNumber):
        print("Verify begin." + "\n")
        T = 0
        F = 0
        CLASSES_PATH = os.path.dirname(os.path.abspath(__file__)) + "/" + "20_newsgroups_subset" + "/"
        CLASSES_LIST = os.listdir(CLASSES_PATH)
        for dir in CLASSES_LIST:
            classPath = CLASSES_PATH + dir + "/"
            for item in self.sampledContainer[kth]:
                filepath = classPath + self.__dict__[dir + "files"][item]
                predClass, predProbability = self.predict(filepath, kNumber)
                if predClass == dir:
                    T += 1
                else:
                    F += 1
        print("Cross Verify {}: Precisely predicted: {}, Mistakely predicted: {}".format(kth, T, F))
        print("Verify end." + "\n")

    def predict(self, filepath, kNumber):
        # print("Predict begin." + "\n")
        stopWordsPath = "stopwords.txt"
        g = GetStopWrods()
        stopWords = g.Call(stopWordsPath)
        c = Cut(stopWords)
        wordsList = c.Call(filepath)
        CLASSES_PATH = os.path.dirname(os.path.abspath(__file__)) + "/" + "20_newsgroups_subset" + "/"
        CLASSES_LIST = os.listdir(CLASSES_PATH)
        distanceContainer = []
        indexList = [0 for i in range(len(self.wordsDictionary))]
        # tmpProbability = 1.0
        for i in range(len(wordsList)):
            if wordsList[i] in self.wordsDictionary:
                indexList[self.wordsDictionary[wordsList[i]]] += 1
        npList = np.array(indexList)
        # tmpIndexList = [0 for i in range(len(self.wordsDictionary))]
        for dir in CLASSES_LIST:
            dirMatrix = np.array(self.__dict__[dir + "BoolMatrix"])
            result = list(np.matmul(dirMatrix, npList.T))
            for item in result:
                tmp = [item, dir]
                distanceContainer.append(tmp)
        distanceContainer.sort(key = takeFirst)
        # print("Predict end." + "\n")
        votingDictionary = {}
        for dir in CLASSES_LIST:
            votingDictionary[dir] = 0
        for i in range(kNumber):
            votingDictionary[distanceContainer[i][1]] += 1
        maxClass = ""
        maxNumber = 0.0
        for dir, votingNumber in votingDictionary.items():
            if votingNumber > maxNumber:
                maxClass = dir
                maxNumber = votingNumber
        return maxClass, maxNumber / kNumber

s = SetProbability()
s.train()
predClass, predProbability = s.predict("52558", 50)
print(predClass, predProbability)